﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace SPQueryToSql.ApplicationObjects.CAML
{
    internal class And : BaseOperator, IOperatorContainer
    {
        public List<BaseOperator> Operators { get; private set; }
        public bool IsRoot;

        public And(XContainer element, List<FieldInfo> listFields)
            : this(element, listFields, false)
        {
        }

        public And(XContainer element, List<FieldInfo> listFields, bool isRoot)
        {
            IsRoot = isRoot;
            Operators = new List<BaseOperator>();
            foreach (var child in element.Elements())
            {
                switch (child.Name.LocalName)
                {
                    case "And": Operators.Add(new And(child, listFields)); break;
                    case "Or": Operators.Add(new Or(child, listFields)); break;
                    case "BeginsWith": Operators.Add(new BeginsWith(child, listFields)); break;
                    case "Contains": Operators.Add(new Contains(child, listFields)); break;
                    case "DateRangesOverlap": Operators.Add(new DateRangesOverlap(child, listFields)); break;
                    case "Eq": Operators.Add(new Eq(child, listFields)); break;
                    case "Geq": Operators.Add(new Geq(child, listFields)); break;
                    case "Gt": Operators.Add(new Gt(child, listFields)); break;
                    case "In": Operators.Add(new In(child, listFields)); break;
                    case "Includes": Operators.Add(new Includes(child, listFields)); break;
                    case "IsNotNull": Operators.Add(new IsNotNull(child, listFields)); break;
                    case "IsNull": Operators.Add(new IsNull(child, listFields)); break;
                    case "Leq": Operators.Add(new Leq(child, listFields)); break;
                    case "Lt": Operators.Add(new Lt(child, listFields)); break;
                    case "Neq": Operators.Add(new Neq(child, listFields)); break;
                    case "NotIncludes": Operators.Add(new NotIncludes(child, listFields)); break;
                }
            }
            if (Operators.Count == 1 && Operators.OfType<IOperatorContainer>().Count() == 1)
            {
                IsRoot = true;
            }
        }

        public override string ToString()
        {
            return ToString(0);
        }

        public override string ToString(int tabQnt)
        {
            var sb = new StringBuilder();
            const string tab = @"    ";
            for (var i = 0; i < tabQnt; i++) sb.Append(tab);
            sb.Append("<And>");
            sb.Append(Environment.NewLine);
            foreach (var op in Operators)
            {
                sb.Append(op.ToString(tabQnt + 1));
            }
            for (var i = 0; i < tabQnt; i++) sb.Append(tab);
            sb.Append("</And>");
            sb.Append(Environment.NewLine);
            return sb.ToString();
        }

        public override string ToSql(Query query)
        {
            var sql = new StringBuilder();
            // If there is only one predicate just return its sql presentation
            if (Operators.Count == 1)
            {
                sql.Append(Operators.First().ToSql(query));
            }
            else
            {
                var sqlLeft = Operators.First().ToSql(query);
                var sqlRight = Operators.Last().ToSql(query);
                sql.AppendFormat("({0} AND {1})", sqlLeft, sqlRight);
            }
            return sql.ToString();
        }
    }
}
